package com.falm.picturebackend.service.impl;

import cn.hutool.core.lang.Pair;
import com.falm.picturebackend.entity.BO.FileMetadataBO;
import com.falm.picturebackend.entity.BO.FileProcessingRecordsBO;
import com.falm.picturebackend.entity.BO.FilesBO;
import com.falm.picturebackend.entity.DTO.FileAddDTO;
import com.falm.picturebackend.entity.constant.FileConstant;
import com.falm.picturebackend.entity.enums.YNEnum;
import com.falm.picturebackend.exception.ErrorCode;
import com.falm.picturebackend.exception.ThrowUtils;
import com.falm.picturebackend.repository.FileRepository;
import com.falm.picturebackend.service.FileService;
import com.falm.picturebackend.util.MinioUtil;
import io.minio.errors.*;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.tika.Tika;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 文件服务实现类 提供文件上传、删除、查询等核心功能
 */
@Service
public class FileServiceImpl implements FileService {

    private static final Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);

    private static final Tika TIKA = new Tika();

    @Resource
    private FileRepository fileRepository;

    @Resource
    private MinioUtil minioUtil;

    @Value("${file.rootPath:/}")
    private String rootPath;

    @Override
    public void uploadFile(FileAddDTO fileAddDTO)
        throws IOException, ServerException, InsufficientDataException, ErrorResponseException,
        NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {

        // 验证并转换文件路径
        String parentFilePath = validateAndConvertFilePath(fileAddDTO.getParentFilePath());

        // 初始化文件夹结构
        Pair<Integer, String> folderInfo = initFolder(parentFilePath);

        // 上传文件到存储系统
        MultipartFile file = fileAddDTO.getFile();
        String storagePath = minioUtil.uploadFile(file);

        // 构建文件信息对象
        FilesBO fileBO = buildFileInfo(file, folderInfo, storagePath, fileAddDTO.getParentFilePath());

        // 构建文件元信息
        FileMetadataBO fileMeta = buildFileMeta(fileBO);

        // 构建文件处理记录
        FileProcessingRecordsBO fileProcessingRecordsBO = buildFileProcessingRecords(fileBO);

        // 批量插入文件信息
        fileRepository.batchInsert(Collections.singletonList(fileBO));
    }

    private FileProcessingRecordsBO buildFileProcessingRecords(FilesBO fileBO) {
        // todo 构建文件处理记录,需要区分管理员与非管理员
        return null;
    }

    private FileMetadataBO buildFileMeta(FilesBO fileBO) {
        FileMetadataBO fileMeta = new FileMetadataBO();

        List<String> tagList = generateFileTag(fileBO.getStoragePath());
        // todo 完善文件元数据，可以添加标签等信息
        return null;
    }

    private List<String> generateFileTag(String storagePath) {

        return null;
    }

    @Override
    public void deleteFile(String filePath) {
        FilesBO fileToDelete = fileRepository.getFileByFullPath(filePath);
        if (ObjectUtils.isEmpty(fileToDelete)) {
            logger.warn("文件不存在:[{}]", filePath);
            return;
        }

        List<FilesBO> allFiles = fileRepository.getAllFileS();
        for (FilesBO file : allFiles) {
            FilesBO targetFile = doDfsSearch(file, filePath);
            if (ObjectUtils.isNotEmpty(targetFile)) {
                doBfsDelete(targetFile);
            }
        }
    }

    @Override
    public List<FilesBO> getAllFileS() {
        return fileRepository.getAllFileS();
    }

    /**
     * 验证并转换文件路径格式
     * 
     * @param filePath 原始文件路径
     * @return 转换后的文件路径
     */
    private String validateAndConvertFilePath(String filePath) {
        if (StringUtils.isNotBlank(filePath)) {
            filePath = filePath.replaceAll("\\\\", "/");
            if (!filePath.endsWith("/")) {
                filePath = filePath.concat("/");
            }
            ThrowUtils.throwIf(!Pattern.matches(FileConstant.FILE_PATH_PATTERN, filePath), ErrorCode.PARAMS_ERROR,
                "文件路径格式错误");
        }
        return filePath;
    }

    /**
     * 构建文件信息对象
     * 
     * @param file 上传的文件
     * @param folderInfo 文件夹信息
     * @param storagePath 存储路径
     * @param parentFilePath 父级文件路径
     * @return 文件信息对象
     */
    private FilesBO buildFileInfo(MultipartFile file, Pair<Integer, String> folderInfo, String storagePath,
        String parentFilePath) throws IOException {
        FilesBO fileBO = new FilesBO();
        fileBO.setUuid(UUID.randomUUID().toString());
        fileBO.setName(file.getOriginalFilename());
        fileBO.setIsFolder(Integer.valueOf(YNEnum.NO.getCode()));
        fileBO.setParentUuid(folderInfo.getValue());
        fileBO.setFileSize(file.getSize());
        fileBO.setFileExtension(getFileExtension(file));
        fileBO.setMimeType(TIKA.detect(file.getInputStream()));
        fileBO.setStoragePath(storagePath);
        fileBO.setStorageType(FileConstant.FILE_STORAGE_TYPE_OSS);
        fileBO.setLevel(folderInfo.getKey() + 1);
        fileBO.setFullPath(StringUtils.join(parentFilePath, file.getOriginalFilename()));
        return fileBO;
    }

    /**
     * 获取文件扩展名
     * 
     * @param file 文件对象
     * @return 文件扩展名
     */
    private String getFileExtension(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        if (originalFilename != null && originalFilename.contains(".")) {
            return originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
        }
        return " ";
    }

    /**
     * 初始化文件夹结构
     * 
     * @param parentFilePath 父级文件路径
     * @return 文件夹层级和UUID信息
     */
    private Pair<Integer, String> initFolder(String parentFilePath) {
        if (StringUtils.isBlank(parentFilePath)) {
            return initRootFolder();
        }

        List<String> folderList = Arrays.asList(parentFilePath.split("/"));
        String parentUuid = " ";
        String fullPath = "";
        int level = 0;
        List<FilesBO> filesBOList = new ArrayList<>();

        for (String folder : folderList) {
            // 构建完整路径
            if (level != 0) {
                fullPath = fullPath + folder + "/";
            } else {
                fullPath = rootPath;
            }

            // 检查文件夹是否已存在
            FilesBO existingFolder = fileRepository.getFileByFullPath(fullPath);
            if (ObjectUtils.isNotEmpty(existingFolder)) {
                parentUuid = existingFolder.getUuid();
                level = existingFolder.getLevel() + 1;
                continue;
            }

            // 创建新的文件夹对象
            FilesBO folderBO = new FilesBO();
            folderBO.setFullPath(fullPath);
            String uuid = UUID.randomUUID().toString();
            folderBO.setUuid(uuid);
            folderBO.setName(folder);
            folderBO.setIsFolder(Integer.valueOf(YNEnum.YES.getCode()));

            if (level == 0) {
                folderBO.setParentUuid(" ");
            } else {
                folderBO.setParentUuid(parentUuid);
            }

            parentUuid = uuid;
            folderBO.setLevel(level++);
            filesBOList.add(folderBO);
        }

        if (!filesBOList.isEmpty()) {
            fileRepository.batchInsert(filesBOList);
        }

        return new Pair<>(level, parentUuid);
    }

    /**
     * 初始化根文件夹
     * 
     * @return 根文件夹层级和UUID信息
     */
    private Pair<Integer, String> initRootFolder() {
        // 根据根路径的绝对路径查询，如果存在，则直接跳过
        FilesBO filesBO = fileRepository.getFileByFullPath(rootPath);
        if (ObjectUtils.isNotEmpty(filesBO)) {
            return new Pair<>(filesBO.getLevel(), filesBO.getUuid());
        }

        // 如果不存在，默认创建一个根路径的文件夹
        FilesBO rootFilesBO = new FilesBO();
        String uuid = UUID.randomUUID().toString();
        rootFilesBO.setUuid(uuid);
        rootFilesBO.setName(" ");
        rootFilesBO.setParentUuid(" ");
        rootFilesBO.setIsFolder(Integer.valueOf(YNEnum.YES.getCode()));
        rootFilesBO.setLevel(0);
        rootFilesBO.setFullPath(rootPath);
        fileRepository.batchInsert(Collections.singletonList(rootFilesBO));
        return new Pair<>(0, uuid);
    }

    /**
     * 深度优先搜索文件
     * 
     * @param root 根节点
     * @param filePath 完整路径
     * @return 文件信息
     */
    private FilesBO doDfsSearch(FilesBO root, String filePath) {
        if (StringUtils.isBlank(filePath)) {
            return root;
        }

        if (StringUtils.equals(root.getFullPath(), filePath)) {
            return root;
        }

        FilesBO result = null;
        for (FilesBO child : root.getChildren()) {
            result = doDfsSearch(child, filePath);
            if (result != null) {
                break;
            }
        }
        return result;
    }

    /**
     * 广度优先删除文件
     * 
     * @param root 根节点
     */
    private void doBfsDelete(FilesBO root) {
        if (ObjectUtils.isEmpty(root)) {
            return;
        }

        Queue<FilesBO> queue = new LinkedList<>();
        queue.add(root);

        while (!queue.isEmpty()) {
            FilesBO node = queue.poll();
            fileRepository.deleteFileByFullPath(node.getFullPath());
            List<FilesBO> children = node.getChildren();
            queue.addAll(children);
        }
    }
}